package com.senyint.hip.fhir.provider;

import java.util.ArrayList;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
 
import org.apache.commons.lang3.StringUtils;
 
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.hl7.fhir.r4.model.Procedure;
 
import org.springframework.stereotype.Component;

import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.param.DateRangeParam;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;

/**
 * *******description*******
 * 手术
 * *************************
 *
 * @author lcg
 * @date 2019/07/01 17:46
 */

@Component
public class ProcedureProvider   implements IResourceProvider{
	
	/**
	 * 临时存储数据
	 */
    private Map<Long, Deque<Procedure>> procedures = new HashMap<Long, Deque<Procedure>>();

    private long myNextId = 1;

	@Override
	public Class<Procedure> getResourceType() {
		// TODO Auto-generated method stub
		return Procedure.class;
	}
	
	 @Create()
	 public MethodOutcome createProcedure(@ResourceParam Procedure pr) {

	        long id = myNextId++;

	        String versionId = addNewVersionPr(pr, id);

	        MethodOutcome retVal = new MethodOutcome();
	        retVal.setId(new IdType("Procedure", String.valueOf(id), versionId));

	        // You can also add an OperationOutcome resource to return
	        // This part is optional though:
	        OperationOutcome outcome = new OperationOutcome();
	        outcome.setIdElement(new IdType("Procedure", String.valueOf(id), versionId));
	        outcome.addIssue().setDiagnostics("新增手术成功").setId(String.valueOf(id));
	        retVal.setOperationOutcome(outcome);

	        return retVal;
	    }
	
	
	 @Delete()
	 public MethodOutcome removeProcedure(@IdParam IdType theId) {
	        Long id;
	        try {
	            id = theId.getIdPartAsLong();
	        } catch (DataFormatException e) {
	            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
	        }

	        if (!procedures.containsKey(id)) {
	            throw new ResourceNotFoundException(theId);
	        }

	        procedures.remove(id);

	        MethodOutcome retVal = new MethodOutcome();
	        OperationOutcome outcome = new OperationOutcome();
	        outcome.addIssue().setDiagnostics("删除成功").setId(String.valueOf(id));
	        retVal.setOperationOutcome(outcome);

	        return retVal;
	    }
	 	
	    @Update()
	    public MethodOutcome updateProcedure(@IdParam IdType theId, @ResourceParam Procedure pr) {

	        Long id;
	        try {
	            id = theId.getIdPartAsLong();
	        } catch (DataFormatException e) {
	            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
	        }

	        /*
	         * Throw an exception (HTTP 404) if the ID is not known
	         */
	        if (!procedures.containsKey(id)) {
	            throw new ResourceNotFoundException(theId);
	        }

	        String versionId = addNewVersionPr(pr, id);

	        MethodOutcome retVal = new MethodOutcome();
	        retVal.setId(new IdType("Procedure", String.valueOf(id), versionId));

	        // You can also add an OperationOutcome resource to return
	        // This part is optional though:
	        OperationOutcome outcome = new OperationOutcome();
	        outcome.setIdElement(new IdType("Procedure", String.valueOf(id), versionId));
	        outcome.addIssue().setDiagnostics("更新手术成功").setId(String.valueOf(id));
	        retVal.setOperationOutcome(outcome);

	        return retVal;
	    }
	    
	    
	    
	    
	    @History()
	    public List<Procedure> readEncounterBundle(@IdParam IdType theId) {
	        Deque<Procedure> retVal;
	        try {
	            retVal =procedures.get(theId.getIdPartAsLong());
	        } catch (NumberFormatException e) {
	            /*
	             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
	             */
	            throw new ResourceNotFoundException(theId);
	        }

	        List<Procedure> prList = new ArrayList<>();
	        for (Procedure pr : retVal) {
	        	prList.add(pr);
	        }

	        return prList;
	    }
	    
	    
	    
	    
	    @Search
	    public List<Procedure> findDrUsingArbitraryCtriteria() {
	        LinkedList<Procedure> retVal = new LinkedList<Procedure>();

	        for (Deque<Procedure> nextPrList : procedures.values()) {
	        	Procedure nextPr = nextPrList.getLast();
	            retVal.add(nextPr);
	        }

	        return retVal;
	    }
	    
	    
	    
	    @Read(version = true)
	    public Procedure readAppointment(@IdParam IdType theId) {
	        Deque<Procedure> retVal;
	        try {
	            retVal = procedures.get(theId.getIdPartAsLong());
	        } catch (NumberFormatException e) {
	            /*
	             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
	             */
	            throw new ResourceNotFoundException(theId);
	        }

	        if (theId.hasVersionIdPart() == false) {
	            return retVal.getLast();
	        } else {
	            for (Procedure nextVersion : retVal) {
	                String nextVersionId = nextVersion.getIdElement().getVersionIdPart();
	                if (theId.getVersionIdPart().equals(nextVersionId)) {
	                    return nextVersion;
	                }
	            }
	            // No matching version
	            throw new ResourceNotFoundException("Unknown version: " + theId.getValue());
	        }

	    }
	    
	    
	    
	    
	    
	    //通过手术信息的患者 ID、 时间段进行查询
	    
	    @Search
	    public List<Procedure> findByIdAndTime(@RequiredParam(name = "patient")  String patientId,@RequiredParam(name = "date")   DateRangeParam date) {

	        LinkedList<Procedure> retVal = new LinkedList<Procedure>();
	        Date startTime = date.getLowerBoundAsInstant();
	        Date endTime = date.getUpperBoundAsInstant();

	        NAMELOOP:
	        for (Deque<Procedure> nextPrlist : procedures.values()) {
	        	Procedure nextPr = nextPrlist.getLast();
	        	
	        	String pId = nextPr.getSubject().getReference();

	        	//pId = StringUtils.substringAfter(pId, "/");
	            long start = nextPr.getPerformedPeriod().getStart().getTime();
	            
	            long end = nextPr.getPerformedPeriod().getEnd().getTime();
	             
	            
	            if(patientId.equals(pId) && startTime.getTime() <= start && endTime.getTime() >= end) {
	            	
	            	 retVal.add(nextPr);
                     break NAMELOOP;
	            }
	        }

	        return retVal;
	    }
	    
	    
	    //通过手术信息的患者 ID、 手术操作的名称进行查询
	    @Search
	    public List<Procedure> findByIdAndOpName(@RequiredParam(name = "patient")  String patientId,@RequiredParam(name = "name") String opName) {

	        LinkedList<Procedure> retVal = new LinkedList<Procedure>();

	      
	        for (Deque<Procedure> nextPrlist : procedures.values()) {
	        	Procedure nextPr = nextPrlist.getLast();
	        	
	        	String pId = nextPr.getSubject().getReference();

	        	//pId = StringUtils.substringAfter(pId, "/");
	        	
	        	String op = nextPr.getCode().getText();
	        	

	            if(patientId.equals(pId) && opName.equals(op)) {
	            	
	            	 retVal.add(nextPr);
                     continue;
	            }
	        }

	        return retVal;
	    }
	    
	    //通过手术信息的患者 ID、 手术操作的编码进行查询
	   @Search
	    public List<Procedure> findByIdAndOpCodee(@RequiredParam(name = "patient")  String patientId,@RequiredParam(name = "code") String opCode) {

	        LinkedList<Procedure> retVal = new LinkedList<Procedure>();

	        NAMELOOP:
	        for (Deque<Procedure> nextPrlist : procedures.values()) {
	        	Procedure nextPr = nextPrlist.getLast();
	        	
	        	String pId = nextPr.getSubject().getReference();

	        	//pId = StringUtils.substringAfter(pId, "/");
	        	
	        	String op = nextPr.getCode().getCodingFirstRep().getCode();
	        	

	            if(patientId.equals(pId) && opCode.equals(op)) {
	            	
	            	 retVal.add(nextPr);
                     break NAMELOOP;
	            }
	        }

	        return retVal;
	    }
	    
	    //通过手术信息的患者标识、 时间段以及手术操作的编码进行查询
	   @Search
	    public List<Procedure> findByPatientIdAndOpCodeeAndTime(@RequiredParam(name = "patient")   String patientId,@RequiredParam(name = "code")   String opCode,@RequiredParam(name = "date")   DateRangeParam date) {

	        LinkedList<Procedure> retVal = new LinkedList<Procedure>();
	        Date startTime = date.getLowerBoundAsInstant();
	        Date endTime = date.getUpperBoundAsInstant();

	        NAMELOOP:
	        for (Deque<Procedure> nextPrlist : procedures.values()) {
	        	Procedure nextPr = nextPrlist.getLast();
	        	
	        	String pId = nextPr.getSubject().getReference();

	        //	pId = StringUtils.substringAfter(pId, "/");
	            long start = nextPr.getPerformedPeriod().getStart().getTime();
	            
	            long end = nextPr.getPerformedPeriod().getEnd().getTime();
	            //手术编码
	        	String op = nextPr.getCode().getCodingFirstRep().getCode();
	            
	            if(patientId.equals(pId) && opCode.equals(op) && startTime.getTime() <= start && endTime.getTime() >= end ) {
	            	
	            	 retVal.add(nextPr);
                     break NAMELOOP;
	            }
	        }

	        return retVal;
	    }
	    
	

	    private String addNewVersionPr(Procedure pr, long id) {

	        Date publishedDate;

	        if (!procedures.containsKey(id)) {
	        	procedures.put(id, new LinkedList<Procedure>());
	            publishedDate = InstantDt.withCurrentTime().getValue();
	        } else {
	        	Procedure oldPr = procedures.get(id).getLast();
	            publishedDate = oldPr.getMeta().getLastUpdated();

	        }


	        pr.getMeta().setLastUpdated(InstantDt.withCurrentTime().getValue());
	        Deque<Procedure> existingVersions = procedures.get(id);

	        String newVersion = Integer.toString(existingVersions.size());

	        //版本号
	        pr.getMeta().setVersionId(newVersion);

	        // Create an ID with the new version and assign it back to the resource
	        IdDt newId = new IdDt("Procedure", Long.toString(id), newVersion);
	        pr.setId(newId);

	        existingVersions.add(pr);
	        return newVersion;
	    }

	
}
